package GoPing

import (
	"fmt"
	"net"
	"time"

	"gitee.com/liumou_site/logger"
)

// Ping 方法用於向遠程主機發送ping請求。
// 參數 count 指定發送ping請求的次數。
func (pg *PingSet) Ping(count int) error {
	// 使用 defer 確保連接在函數結束時關閉
	defer func() {
		if pg.Conn != nil {
			if err := pg.Close(); err != nil {
				logger.Error("關閉連接失敗:", err)
			}
		}
	}()

	// 嘗試連接遠程主機
	if err := pg.Dail(); err != nil {
		logger.Error("未找到遠程主機:", err)
		return err
	}

	// 打印ping操作的起始地址
	if pg.Print {
		fmt.Println("Ping from", pg.Conn.LocalAddr())
	}

	// 設置超時時間
	if err := pg.SetDeadline(); err != nil {
		logger.Error("設置超時失敗:", err)
		return err
	}

	// 循環發送ping請求
	for i := 0; i < count; i++ {
		// 發送ping請求並接收響應
		r := sendPingMsg(pg.Conn, pg.Data)

		// 處理響應結果
		if r.Error != nil {
			if netErr, ok := r.Error.(*net.OpError); ok && netErr.Timeout() {
				if pg.Print {
					fmt.Printf("從 %s 回覆: 超時\n", pg.Addr)
				}
				logger.Error("從 %s 回覆: 超時", pg.Addr)
				logger.Info("設置超時:", pg.Timeout)

				// 重新連接遠程主機
				if err := pg.Dail(); err != nil {
					logger.Error("重新連接遠程主機失敗:", err)
					return err
				}
			} else if pg.Print {
				// 其他錯誤
				fmt.Printf("從 %s 回覆: %s\n", pg.Addr, r.Error)
			}
		} else if pg.Print {
			// 響應正常
			fmt.Printf("從 %s 回覆: time=%d ttl=%d\n", pg.Addr, r.Time, r.TTL)
		}

		// 等待一定時間後發送下一個ping請求
		time.Sleep(time.Second)
	}

	return nil
}

// PingCount 發送指定數量的ping請求並收集回覆。
// 參數count表示要發送的ping請求的數量。
// 返回值reply是一個包含所有ping請求回覆的切片。
func (pg *PingSet) PingCount(count int) []Reply {
	// 使用 defer 確保連接在函數結束時關閉
	defer func() {
		if pg.Conn != nil {
			if err := pg.Close(); err != nil {
				logger.Error("關閉連接失敗:", err)
			}
		}
	}()

	var reply []Reply

	// 嘗試連接遠程主機
	if err := pg.Dail(); err != nil {
		logger.Error("未找到遠程主機:", err)
		return reply
	}

	// 設置超時時間
	if err := pg.SetDeadline(); err != nil {
		logger.Error("設置超時失敗:", err)
		return nil
	}

	// 循環發送ping請求
	for i := 0; i < count; i++ {
		// 發送ping請求並獲取回覆
		r := sendPingMsg(pg.Conn, pg.Data)
		// 將回覆添加到結果切片中
		reply = append(reply, r)
		// 休眠1秒，為下一次發送ping請求做準備
		time.Sleep(time.Second)
	}

	// 返回收集到的所有回覆
	return reply
}

// New 創建並返回一個新的 PingSet 實例。
// addr: 目標主機的地址或域名。
// timeout: 超時時間（秒）。
// 返回一個 PingSet 指針和可能的錯誤。
func New(addr string, timeout int) (*PingSet, error) {
	// 將請求編號和數據打包成特定格式的消息
	wb, err := MarshalMsg(8, Data)
	if err != nil {
		return nil, err
	}

	// 解析地址，確保地址的有效性
	addr, err = Lookup(addr)
	if err != nil {
		return nil, err
	}

	// 返回配置好的 PingSet 實例
	return &PingSet{
		Data:    wb,
		Addr:    addr,
		Timeout: timeout,
		Req:     8,
		Print:   true,
	}, nil
}
